package com.tj.bo.payment.server.services;

import com.fqgj.common.utils.DateUtil;
import com.fqgj.common.utils.HttpUtil;
import com.fqgj.common.utils.StringUtils;
import com.fqgj.exception.common.ApplicationException;
import com.google.gson.Gson;
import com.jsoniter.output.JsonStream;
import com.tj.bo.common.conf.util.SerialNumberUtil;
import com.tj.bo.payment.client.PayCommandService;
import com.tj.bo.payment.client.enums.PayOrderStatusEnum;
import com.tj.bo.payment.client.enums.PayOrderTypeEnum;
import com.tj.bo.payment.client.enums.PayResultEnum;
import com.tj.bo.payment.client.vo.*;
import com.tj.bo.payment.server.dao.PaymentOrderDao;
import com.tj.bo.payment.server.entity.PaymentOrderEntity;
import com.tj.bo.payment.server.services.dto.DfPayCreateResultDTO;
import com.tj.bo.payment.server.services.dto.DfPayQueryResultDTO;
import com.tj.bo.payment.server.services.enums.DfPaymentRefCodeEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author ZhangChao
 * @since 2021/7/15
 */
@Service
public class PayCommandServiceImpl implements PayCommandService {
    private final Logger logger = Logger.getLogger(PayCommandServiceImpl.class.getName());
    private final Gson gson = new Gson();
    @Autowired
    private PaymentConfig paymentConfig;
    @Autowired
    private PaymentOrderDao paymentOrderDao;

    @Override
    public PaymentOrderVO createPayOrder(PayApplicationVO payApplicationVO) {
        if (Objects.isNull(payApplicationVO) ||
                Stream.of(payApplicationVO.getPayAmount(), payApplicationVO.getPayBankCode(), payApplicationVO.getProductName()).anyMatch(Objects::isNull)) {
            throw new ApplicationException("payment params are illegal");
        }
        Map<String, String> params = new HashMap<>();
        String payOrderCode = SerialNumberUtil.getBizOrderNumber("P", "M");
        params.put("pay_memberid", paymentConfig.getMerchantId());
        params.put("pay_orderid", payOrderCode);
        params.put("pay_applydate", DateUtil.getDate("yyyy-MM-dd HH:mm:ss"));
        params.put("pay_bankcode", payApplicationVO.getPayBankCode());
        params.put("pay_notifyurl", paymentConfig.getNotifyUrl());
        params.put("pay_callbackurl", paymentConfig.getCallbackUrl());
        params.put("pay_amount", payApplicationVO.getPayAmount().toString());

        logger.info("== 发起支付==,request:" + JsonStream.serialize(params));
        List<String> keys = params.keySet().stream()
                .sorted(String::compareTo)
                .collect(Collectors.toList());
        String stringSignTemp = keys.stream()
                .map(key -> {
                    Object value = params.get(key);
                    return key + "=" + value.toString();
                }).collect(Collectors.joining("&"))
                .concat("&key=" + paymentConfig.getApiKey());
        params.put("pay_md5sign", md5(stringSignTemp));
        params.put("pay_productname", payApplicationVO.getProductName());
        if (StringUtils.isNotEmpty(payApplicationVO.getAttach())) {
            params.put("pay_attach", payApplicationVO.getAttach());
        }
        String result = HttpUtil.postPage(paymentConfig.getGateway() + paymentConfig.getPayOrderCreate(), params);
        if (result.startsWith("{")) {
            logger.warning(result);
            throw new ApplicationException("Pay Failed");
        }
        PayUrlWrapperVO payUrlWrapperVO = new PayUrlWrapperVO();
        payUrlWrapperVO.setUrl(paymentConfig.getGateway() + paymentConfig.getPayOrderCreate());
        payUrlWrapperVO.setParams(params);
        payUrlWrapperVO.setHtml(result);
        PaymentOrderVO paymentOrderVO = new PaymentOrderVO();
        paymentOrderVO.setOrderCode(payOrderCode);
        paymentOrderVO.setPayUrlWrapper(payUrlWrapperVO);
        PaymentOrderEntity entity = new PaymentOrderEntity();
        entity.setOrderCode(payOrderCode);
        entity.setAmount(new BigDecimal(payApplicationVO.getPayAmount()));
        entity.setRemark(payApplicationVO.getAttach());
        entity.setType(PayOrderTypeEnum.PAYMENT.getCode());
        paymentOrderDao.insert(entity);
        return paymentOrderVO;
    }

    @Override
    public PaymentOrderVO createDfPayOrder(DfPayApplicationVO dfPayApplicationVO) {
        logger.info("创建代付订单开始");
        if (Objects.isNull(dfPayApplicationVO) ||
                Stream.of(dfPayApplicationVO.getBankName(), dfPayApplicationVO.getAccountName(), dfPayApplicationVO.getCardNumber(),
                        dfPayApplicationVO.getProvince(), dfPayApplicationVO.getCity(), dfPayApplicationVO.getMoney(),
                        dfPayApplicationVO.getSubbranch()).anyMatch(Objects::isNull)) {
            throw new ApplicationException("df payment params are illegal");
        }
        Map<String, String> params = new HashMap<>();
        String payOrderCode = SerialNumberUtil.getBizOrderNumber("D", "F");
        params.put("mchid", paymentConfig.getMerchantId());
        params.put("out_trade_no", payOrderCode);
        params.put("money", dfPayApplicationVO.getMoney().toString());
        params.put("bankname", dfPayApplicationVO.getBankName());
        params.put("subbranch", dfPayApplicationVO.getSubbranch());
        params.put("accountname", dfPayApplicationVO.getAccountName());
        params.put("cardnumber", dfPayApplicationVO.getCardNumber());
        params.put("province", dfPayApplicationVO.getProvince());
        params.put("city", dfPayApplicationVO.getCity());
        List<String> keys = params.keySet().stream()
                .sorted(String::compareTo)
                .collect(Collectors.toList());
        String stringSignTemp = keys.stream()
                .map(key -> {
                    Object value = params.get(key);
                    return key + "=" + value.toString();
                }).collect(Collectors.joining("&"))
                .concat("&key=" + paymentConfig.getApiKey());
        params.put("pay_md5sign", md5(stringSignTemp));
        logger.info("创建代付订单参数：" + params);
        String result = HttpUtil.postPage(paymentConfig.getGateway() + paymentConfig.getDfPayOrderCreate(), params);
        logger.info("代付订单创建接口返回：" + result);
        if (StringUtils.isEmpty(result)) {
            throw new ApplicationException("df payment order create failed");
        }
        logger.info("代付订单下单结果：" + result);
        DfPayCreateResultDTO dfPayCreateResultDTO = gson.fromJson(result, DfPayCreateResultDTO.class);
        if (!"success".equals(dfPayCreateResultDTO.getStatus())) {
            throw new ApplicationException("df payment order create failed");
        }
        logger.info("创建代付订单成功：" + payOrderCode);
        PaymentOrderEntity entity = new PaymentOrderEntity();
        entity.setOrderCode(payOrderCode);
        entity.setAmount(new BigDecimal(dfPayApplicationVO.getMoney()));
        entity.setType(PayOrderTypeEnum.DF_PAYMENT.getCode());
        entity.setTransactionId(dfPayCreateResultDTO.getTransactionId());
        entity.setRemark(dfPayApplicationVO.getBusinessCode());
        paymentOrderDao.insert(entity);
        PaymentOrderVO paymentOrderVO = new PaymentOrderVO();
        paymentOrderVO.setOrderCode(payOrderCode);
        return paymentOrderVO;
    }

    @Override
    public PayNotificationResolveVO resolvePayNotification(PayNotificationVO payNotificationVO) {
        logger.info("支付回调开始：" + gson.toJson(payNotificationVO));
        PayNotificationResolveVO resolveVO = new PayNotificationResolveVO();
        if (Objects.isNull(payNotificationVO)) {
            resolveVO.setPayResult(PayResultEnum.FAIL);
            resolveVO.setDes("FAIL");

        } else {
            String stringSignTemp = "amount=" + payNotificationVO.getAmount() + '&' +
                    "datetime=" + payNotificationVO.getDatetime() + '&' +
                    "memberid=" + paymentConfig.getMerchantId() + '&' +
                    "orderid=" + payNotificationVO.getOrderId() + '&' +
                    "returncode=" + payNotificationVO.getReturnCode() + '&' +
                    "transaction_id=" + payNotificationVO.getTransactionId() + '&' +
                    "key=" + paymentConfig.getApiKey();
            String md5sign = md5(stringSignTemp);
            if (Objects.equals(md5sign, payNotificationVO.getSign())) {
                logger.info("支付验证成功");
                PaymentOrderEntity entity = paymentOrderDao.getByOrderCode(payNotificationVO.getOrderId());
                if (Objects.nonNull(entity) && Objects.equals(entity.getStatus(), PayOrderStatusEnum.NOT_PAY.getCode())) {
                    entity.setTradeTime(DateUtil.getDate(payNotificationVO.getDatetime(), "yyyyMMddHHmmss"));
                    entity.setTransactionId(payNotificationVO.getTransactionId());
                    if (Objects.equals("00", payNotificationVO.getReturnCode())) {
                        entity.setStatus(PayOrderStatusEnum.PAID.getCode());
                        resolveVO.setPayResult(PayResultEnum.SUCCESS);
                        resolveVO.setDes("OK");

                        BusinessVO businessVO = new BusinessVO();
                        businessVO.setAmount(new BigDecimal(payNotificationVO.getAmount()).intValue());
                        businessVO.setAttach(payNotificationVO.getAttach());
                        businessVO.setPayOrderCode(payNotificationVO.getOrderId());
                        businessVO.setTransactionId(payNotificationVO.getTransactionId());
                        resolveVO.setBusiness(businessVO);
                    } else {
                        entity.setStatus(PayOrderStatusEnum.CANCELED.getCode());
                        resolveVO.setPayResult(PayResultEnum.FAIL);
                        resolveVO.setDes("PAY FAILED");
                    }
                    paymentOrderDao.updateByPrimaryKey(entity);
                } else {
                    logger.warning(String.format("订单%s不存在", payNotificationVO.getTransactionId()));
                    resolveVO.setPayResult(PayResultEnum.FAIL);
                    resolveVO.setDes("SYSTEM ERROR");
                }
            } else {
                logger.info("支付验证失败");
                resolveVO.setDes("VERIFICATION FAILED");
                throw new ApplicationException("VERIFICATION FAILED");
            }
        }
        return resolveVO;
    }

    @Override
    public DfPaymentOrderResultVO syncDfPayOrder(String dfPaymentOrderCode) {
        logger.info("代付订单同步开始，订单号：" + dfPaymentOrderCode);
        PaymentOrderEntity entity = paymentOrderDao.getByOrderCode(dfPaymentOrderCode);
        if (Objects.isNull(entity)) {
            throw new ApplicationException("df pay order is not exist");
        }
        Map<String, String> params = new HashMap<>();
        params.put("mchid", paymentConfig.getMerchantId());
        params.put("out_trade_no", dfPaymentOrderCode);
        List<String> keys = params.keySet().stream()
                .sorted(String::compareTo)
                .collect(Collectors.toList());
        String stringSignTemp = keys.stream()
                .map(key -> {
                    Object value = params.get(key);
                    return key + "=" + value.toString();
                }).collect(Collectors.joining("&"))
                .concat("&key=" + paymentConfig.getApiKey());
        params.put("pay_md5sign", md5(stringSignTemp));
        String result = HttpUtil.postPage(paymentConfig.getGateway() + paymentConfig.getDfPayOrderCreate(), params);
        if (StringUtils.isEmpty(result)) {
            throw new ApplicationException("df pay order query failed");
        }
        logger.info("代付订单查询结果：" + result);
        DfPayQueryResultDTO dfPayQueryResultDTO = gson.fromJson(result, DfPayQueryResultDTO.class);
        if (!"success".equals(dfPayQueryResultDTO.getStatus())) {
            throw new ApplicationException("df pay order query failed");
        }
        DfPaymentOrderResultVO resultVO = new DfPaymentOrderResultVO();
        DfPaymentRefCodeEnum codeEnum = DfPaymentRefCodeEnum.getEnum(dfPayQueryResultDTO.getRefCode());
        if (Objects.isNull(codeEnum)) {
            throw new ApplicationException("df pay order query failed");
        }
        switch (codeEnum) {
            case SUCCESS:
                entity.setStatus(PayOrderStatusEnum.PAID.getCode());
                entity.setTradeTime(DateUtil.getDate(dfPayQueryResultDTO.getSuccessTime(), "yyyy-MM-dd HH:mm:ss"));
                paymentOrderDao.updateByPrimaryKey(entity);
                resultVO.setStatus(PayOrderStatusEnum.PAID);
                break;
            case FAILED:
            case REVIEW_REJECTED:
                entity.setStatus(PayOrderStatusEnum.CANCELED.getCode());
                paymentOrderDao.updateByPrimaryKey(entity);
                resultVO.setStatus(PayOrderStatusEnum.CANCELED);
                break;
            case ORDER_NOT_EXIST:
                throw new ApplicationException("query df pay order is not exist");
            case UNKNOWN:
                throw new ApplicationException("df pay order query failed");
            default:
                entity.setStatus(PayOrderStatusEnum.NOT_PAY.getCode());
                break;
        }
        BusinessVO businessVO = new BusinessVO();
        businessVO.setTransactionId(entity.getTransactionId());
        businessVO.setAmount(entity.getAmount().intValue());
        businessVO.setAttach(entity.getRemark());
        businessVO.setPayOrderCode(entity.getOrderCode());
        resultVO.setBusinessVO(businessVO);
        return resultVO;
    }

    public static String md5(String str) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update(str.getBytes());
            byte[] byteDigest = md.digest();
            int i;
            //字符数组转换成字符串
            StringBuffer buf = new StringBuffer("");
            for (int offset = 0; offset < byteDigest.length; offset++) {
                i = byteDigest[offset];
                if (i < 0) {
                    i += 256;
                }
                if (i < 16) {
                    buf.append("0");
                }
                buf.append(Integer.toHexString(i));
            }
            // 32位加密
            return buf.toString().toUpperCase();
            // 16位的加密
            //return buf.toString().substring(8, 24).toUpperCase();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            return null;
        }
    }

    public static void main(String[] args) {
        Map<String, Object> params = new HashMap<>();
        String payOrderCode = SerialNumberUtil.getBizOrderNumber("P", "M");
        params.put("pay_memberid", 210796892);
        params.put("pay_orderid", payOrderCode);
        params.put("pay_applydate", "2021-07-16 14:55:35");
        params.put("pay_bankcode", "975");
        params.put("pay_notifyurl", "http://47.98.115.75:8112/notify");
        params.put("pay_callbackurl", "http://47.98.115.75:8112/notify");
        params.put("pay_amount", 100);
        List<String> keys = params.keySet().stream()
                .sorted(String::compareTo)
                .collect(Collectors.toList());
        String stringSignTemp = keys.stream()
                .map(key -> {
                    Object value = params.get(key);
                    return key + "=" + value.toString();
                }).collect(Collectors.joining("&"))
                .concat("&key=0bo4eg8a28otnlndo5qjvasceo17gjfj");
        params.put("pay_md5sign", md5(stringSignTemp));
        //System.out.println(stringSignTemp);
        System.out.println(params);
    }
}
